module Test
  module Unit
    class TestResult
      attr_reader :pendings

      alias_method(:initialize_without_pendings, :initialize)
      def initialize
        initialize_without_pendings
        @pendings = []
      end

      def add_pending(pending)
        @pendings << pending
        notify_listeners(FAULT, pending)
        notify_listeners(CHANGED, self)
      end

      def pending_count
        @pendings.size
      end

      alias_method(:to_s_without_pendings, :to_s)
      def to_s
        to_s_without_pendings + ", #{pending_count} pendings"
      end
    end

    class Pending
      include Util::BacktraceFilter
      attr_reader :test_name, :location, :message
      
      SINGLE_CHARACTER = 'P'

      # Creates a new Pending with the given location and
      # message.
      def initialize(test_name, location, message)
        @test_name = test_name
        @location = location
        @message = message
      end
      
      # Returns a single character representation of a pending.
      def single_character_display
        SINGLE_CHARACTER
      end

      # Returns a brief version of the error description.
      def short_display
        "#@test_name: #{@message.split("\n")[0]}"
      end

      # Returns a verbose version of the error description.
      def long_display
        location_display = filter_backtrace(location)[0]
        location_display = location_display.sub(/\A(.+:\d+).*/, ' [\\1]')
        "Pending:\n#{@test_name}#{location_display}:\n#{@message}"
      end

      # Overridden to return long_display.
      def to_s
        long_display
      end
    end

    class PendedError < StandardError
    end

    module AssertionsWithPend
      def pend(message="Pended", &block)
        if block_given?
          begin
            yield
          rescue Exception
            raise PendedError, message, $!.backtrace
          end
          flunk("Pending block should not be passed: #{message}")
        else
          raise PendedError.new(message)
        end
      end
    end

    class TestCase
      include AssertionsWithPend

      alias_method(:add_error_without_pending, :add_error)
      def add_error(exception)
        if exception.is_a?(PendedError)
          pending = Pending.new(name, exception.backtrace, exception.message)
          @_result.add_pending(pending)
        else
          add_error_without_pending(exception)
        end
      end
    end
  end
end
